package com.gitee.huanminabc.jcommon.reflect;


import com.gitee.huanminabc.jcommon.str.StringUtil;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.*;

/**
 * 简要描述
 *
 * @Author: huanmin
 * @Date: 2022/11/30 9:50
 * @Version: 1.0
 * @Description: 文件作用详细描述....
 */
public final class FieldUtil {

    /**
     * 获取指定类的所有的field,包括父类
     *
     * @param clazz 字段所属类型
     */
    public static Field[] getFields(Class<?> clazz) {
        List<Field> fields = new ArrayList<>(32);
        while (Object.class != clazz && clazz != null) {
            // 获得该类所有声明的字段，即包括public、private和protected，但是不包括父类的申明字段，
            // getFields：获得某个类的所有的公共（public）的字段，包括父类中的字段
            for (Field field : clazz.getDeclaredFields()) {
                ReflectUtil.setAccessible(field);
                fields.add(field);
            }
            clazz = clazz.getSuperclass();
        }
        return fields.toArray(new Field[0]);
    }

    //获取指定类的所有的public字段,包括父类
    public static Field[] getPublicFields(Class<?> clazz) {
        List<Field> fields = new ArrayList<>(32);
        while (Object.class != clazz && clazz != null) {
            // 获得该类所有声明的字段，即包括public、private和protected，但是不包括父类的申明字段，
            // getFields：获得某个类的所有的公共（public）的字段，包括父类中的字段
            for (Field field : clazz.getFields()) {
                ReflectUtil.setAccessible(field);
                fields.add(field);
            }
            clazz = clazz.getSuperclass();
        }
        return fields.toArray(new Field[0]);
    }

    /**
     * 获取字段值
     *
     * @param field  字段
     * @param target 字段所属实例对象
     * @return 字段值
     */
    public static Object getFieldValue(Field field, Object target) {
        ReflectUtil.setAccessible(field);
        try {
            return field.get(target);
        } catch (Exception e) {
            throw new IllegalStateException(String.format("获取%s对象的%s字段值错误!"
                    , target.getClass().getName(), field.getName()), e);
        }
    }


    /**
     * 获取对象中指定field值
     *
     * @param obj       对象
     * @param fieldName 字段名
     * @return 字段值
     */
    public static Object getFieldValue(Object obj, String fieldName) {
        //如果是基本类型那么就直接返回就行
        if (ClassIdentifyUtil.isPrimitive(obj.getClass())) {
            return obj;
        }
        return getFieldValue(getField(obj.getClass(), fieldName), obj);
    }


    /**
     * 设置字段值
     *
     * @param field  字段
     * @param target 字段所属对象实例
     * @param value  需要设置的值
     */
    public static void setFieldValue(Object target, Field field, Object value) {
        ReflectUtil.setAccessible(field);
        try {
            value = ObjectUtil.convertValueType(field.getType(), value);
            field.set(target, value);
        } catch (Exception e) {
            throw new IllegalStateException(String.format("设置%s对象的%s字段值错误 value: %s", target.getClass().getName(), field.getName(), value), e);
        }
    }


    //设置对象中指定字段值
    public static void setFieldValue(Object target, String fieldName, Object value) {
        //如果是基本类型那么就直接返回就行
        if (ClassIdentifyUtil.isPrimitive(target.getClass())) {
            return;
        }
        setFieldValue(target, getField(target.getClass(), fieldName), value);
    }


    /**
     * 获取指定对象中指定字段路径的值(类似js访问对象属性) <br/>
     * 如：Product p = new Product(new User())  <br/>
     * 可使用ReflectionUtils.getValueByFieldPath(p, "user.name")获取到用户的name属性
     *
     * @param obj       取值对象
     * @param fieldPath 字段路径(形如 user.name)
     * @return 字段value
     */
    public static Object getValueByFieldPath(Object obj, String fieldPath) {
        String[] fieldNames = fieldPath.split("\\.");
        Object result = null;
        for (String fieldName : fieldNames) {
            result = getFieldValue(obj, fieldName);
            if (result == null) {
                return null;
            }
            obj = result;
        }
        return result;
    }


    //通过方法名获属性名称
    public static String methodToField(String methodName) {
        if (methodName.startsWith("is")) {
            methodName = methodName.substring(2);
        } else if (methodName.startsWith("get") || methodName.startsWith("set")) {
            methodName = methodName.substring(3);
        } else {
            throw new RuntimeException("Error parsing property name '" + methodName + "'.  Didn't start with 'is', 'get' or 'set'.");
        }
        if (methodName.length() == 1 || methodName.length() > 1 && !Character.isUpperCase(methodName.charAt(1))) {
            methodName = methodName.substring(0, 1).toLowerCase(Locale.ENGLISH) + methodName.substring(1);
        }
        return methodName;
    }


    public static Field getField(final Class<?> cls, final String fieldName) {
        return getField(cls, fieldName, false);
    }

    public static Field getField(final Class<?> cls, final String fieldName, boolean ignoreCase) {
        for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) {
            try {
                final Field field = acls.getDeclaredField(fieldName);
                if (!Modifier.isPublic(field.getModifiers())) {
                    field.setAccessible(true);
                }
                return field;
            } catch (final NoSuchFieldException ignore) { // NOPMD
                //如果是忽略大小写那么就尝试获取忽略大小写的字段
                if (ignoreCase) {
                    for (Field declaredField : acls.getDeclaredFields()) {
                        if (declaredField.getName().equalsIgnoreCase(fieldName)) {
                            if (!Modifier.isPublic(declaredField.getModifiers())) {
                                declaredField.setAccessible(true);
                            }
                            return declaredField;
                        }
                    }
                }
            }
        }

        //接口里面的常亮
        Field match = null;
        for (final Class<?> class1 : ClassUtil.getAllInterfaces(cls)) {
            try {
                match = class1.getField(fieldName);
            } catch (final NoSuchFieldException ignore) { // NOPMD
                //如果是忽略大小写那么就尝试获取忽略大小写的字段
                if (ignoreCase) {
                    for (Field declaredField : class1.getFields()) {
                        if (declaredField.getName().equalsIgnoreCase(fieldName)) {
                            match = declaredField;
                            break;
                        }
                    }
                }
            }
        }
        return match;
    }

    public static Field getFieldAuto(final Class<?> cls, String fieldName) {
        //尝试获取
        Field field = getField(cls, fieldName);
        if (field != null) {
            return field;
        }

        //如果没有找到那么就尝试将fieldName转换为下划线命名法
        if (!fieldName.contains("_")) {
            fieldName = StringUtil.toUnderline(fieldName);
            field = getField(cls, fieldName);
            if (field != null) {
                return field;
            }
        }

        //尝试将fieldName转换为驼峰命名法
        fieldName = StringUtil.toCamelCase(fieldName);
        field = getField(cls, fieldName);
        if (field != null) {
            return field;
        }

        //如果还是没有找到那么就尝试将fieldName转换为大写驼峰命名法
        fieldName = StringUtil.toUpperCamelCase(fieldName);
        field = getField(cls, fieldName);
        if (field != null) {
            return field;
        }
        //如果还是没有找到那么就尝试将fieldName转换为大写下划线命名法
        String fieldNameUpperUnderline = StringUtil.toUnderline(fieldName).toUpperCase(Locale.ENGLISH);
        field = getField(cls, fieldNameUpperUnderline);
        if (field != null) {
            return field;
        }
        //如果还是没有找到那么就尝试将fieldName转换为小写
        String fieldNameLowerCamel = fieldName.toLowerCase(Locale.ENGLISH);
        field = getField(cls, fieldNameLowerCamel);
        if (field != null) {
            return field;
        }
        //如果还是没有找到那么就尝试将fieldName转换为大写
        String fieldNameUpperCamelLower = fieldName.toUpperCase(Locale.ENGLISH);
        field = getField(cls, fieldNameUpperCamelLower);

        if (field != null) {
            return field;
        }
        //如果还没有找到那么就忽略大小写
        return getField(cls, fieldName, true);
    }


    private static boolean setEnum(Field field, Object target, Object value) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        //如果是枚举类型那么调用目标的getEnum 方法
        if (field.getType().isEnum()) {
            Object invoke = EnumUtil.castEnum(value, field.getType());
            if (invoke != null) {
                field.set(target, invoke);
                return true;
            }
        }
        return false;
    }


}
